// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#define __STDC_FORMAT_MACROS
#include <inttypes.h>

#include <base/logging.h>
#include <base/stringprintf.h>

#include "nvram.h"
#include "tpmd.h"
#include "volatile_nvram.h"

namespace tpmd {

static const char* kTpmdService = "org.chromium.tpmd";
static const char* kTpmdPath = "/org/chromium/tpmd";
static const char* kNvramError = "org.chromium.tpmd.nvram.error";

Tpmd::Tpmd(DBus::Connection* connection, DBus::BusDispatcher* dispatcher)
    : DBus::ObjectAdaptor(*connection, kTpmdPath, REGISTER_NOW,
                          AVOID_EXCEPTIONS),
      dbus_(connection), dispatcher_(dispatcher),
      nvram_(new VolatileNvram()) { }

Tpmd::~Tpmd() { }

bool Tpmd::Init() {
  try {
    dbus_->request_name(kTpmdService);
  } catch (DBus::Error e) { // NOLINT dbuscxx
    LOG(ERROR) << "can't acquire name: " << e.what()
               << " " << e.name() << " " << e.message();
    return false;
  }
  return true;
}

void Tpmd::Run() {
  dispatcher_->enter();
  while (1)
    dispatcher_->do_iteration();
  // unreachable
  dispatcher_->leave();
}

DBus::Message Tpmd::Encrypt(const DBus::CallMessage& call,
                            const Bytes& plaintext) {
  return EncryptMakeReply(call, plaintext);
}

DBus::Message Tpmd::Decrypt(const DBus::CallMessage& call,
                            const Bytes& ciphertext) {
  return DecryptMakeReply(call, ciphertext);
}

DBus::Message Tpmd::GetStatus(const DBus::CallMessage& call) {
  return GetStatusMakeReply(call, "{}");
}

DBus::Message Tpmd::Allocate(const DBus::CallMessage& call,
                             const uint32_t& slot, const uint32_t& size,
                             const VariantMap& opts) {
  uint32_t flags = 0;
  if (opts.count("LockOnce") && opts.find("LockOnce")->second)
    flags |= Nvram::NVRAM_LOCKONCE;
  if (!nvram_->Allocate(slot, size, flags))
    return DBus::ErrorMessage(call, kNvramError, "Allocate() failed");
  return AllocateMakeReply(call);
}

DBus::Message Tpmd::Free(const DBus::CallMessage& call, const uint32_t& slot) {
  if (!nvram_->Free(slot))
    return DBus::ErrorMessage(call, kNvramError, "Free() failed");
  return FreeMakeReply(call);
}

DBus::Message Tpmd::Write(const DBus::CallMessage& call, const uint32_t& slot,
                          const Bytes& bytes) {
  if (bytes.size() != nvram_->size(slot)) {
    std::string errstr = StringPrintf("Write(%" PRIu32 ") size mismatch: "
                                      "%zu != %zu", slot, bytes.size(),
                                      nvram_->size(slot));
    return DBus::ErrorMessage(call, kNvramError, errstr.c_str());
  }
  if (!nvram_->Write(slot, bytes))
    return DBus::ErrorMessage(call, kNvramError, "Write() failed");
  return WriteMakeReply(call);
}

DBus::Message Tpmd::Read(const DBus::CallMessage& call, const uint32_t& slot) {
  Bytes data;
  if (!nvram_->Read(slot, &data))
    return DBus::ErrorMessage(call, kNvramError, "Read() failed");
  return ReadMakeReply(call, data);
}

DBus::Message Tpmd::IsDefined(const DBus::CallMessage& call,
                              const uint32_t& slot) {
  return IsDefinedMakeReply(call, nvram_->is_defined(slot));
}

DBus::Message Tpmd::IsLocked(const DBus::CallMessage& call,
                             const uint32_t& slot) {
  return IsLockedMakeReply(call, nvram_->is_locked(slot));
}

DBus::Message Tpmd::Size(const DBus::CallMessage& call, const uint32_t& slot) {
  return SizeMakeReply(call, nvram_->size(slot));
}

}  // namespace tpmd
